/**
 * @Description 设备分类查询
 * @method
 * @Date $2023-2-20
 * @Param
 * @return
 **/

package application

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/edgexfoundry/edgex-go/internal/app/core/metadata/container"
	"github.com/edgexfoundry/edgex-go/internal/app/dtos"
	requestDTO "github.com/edgexfoundry/edgex-go/internal/app/dtos/requests"
	"github.com/edgexfoundry/edgex-go/internal/app/infrastructure/redis"
	"github.com/edgexfoundry/edgex-go/internal/app/models"
	conter "github.com/edgexfoundry/edgex-go/internal/core/metadata/container"
	"github.com/edgexfoundry/edgex-go/internal/pkg/correlation"
	bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container"
	"github.com/edgexfoundry/go-mod-bootstrap/v2/di"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/common"
	dtosPro "github.com/edgexfoundry/go-mod-core-contracts/v2/dtos"
	"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
	"github.com/edgexfoundry/go-mod-messaging/v2/pkg/types"
)

/**
 * @Description 获取设备分类列表查询
 * @method
 * @auth kely
 * @Date 2023-02-20
 * @Param
 * @return
 **/

// AllProductCategorys query the AllProductCategorys with offset, limit, and labels
func AllProductCategorys(offset int, limit int, labels []string, dic *di.Container) (productCategorys []dtos.ProductCategory, totalCount uint32, err errors.EdgeX) {
	dbClient := container.DBClientFrom(dic.Get)
	productCategoryModels, err := dbClient.AllproductCategorys(offset, limit, labels)
	if err == nil {
		totalCount = uint32(len(productCategoryModels))
	}
	if err != nil {
		return productCategorys, totalCount, errors.NewCommonEdgeXWrapper(err)
	}
	productCategorys = make([]dtos.ProductCategory, len(productCategoryModels))
	for i, d := range productCategoryModels {
		// 使用order 作为序号
		productCategorys[i] = dtos.FromProductCategoryModelToDTO(d)
	}
	return productCategorys, totalCount, nil
}

/**
 * @Description 产品查询-不分页
 * @method
 * @auth kely
 * @Date 2023-3-08
 * @Param
 * @return
 **/

func ProdcutCategoryOwn(dic *di.Container) (productCategorys []dtos.ProductCategory, totalCount uint32, err errors.EdgeX) {
	dbClient := container.DBClientFrom(dic.Get)
	productCategoryModels, err := dbClient.ProdcutCategoryOwn()
	if err == nil {
		totalCount = uint32(len(productCategoryModels))
	}
	if err != nil {
		return productCategorys, totalCount, errors.NewCommonEdgeXWrapper(err)
	}
	productCategorys = make([]dtos.ProductCategory, len(productCategoryModels))
	for i, p := range productCategoryModels {
		productCategorys[i] = dtos.FromProductCategoryModelToDTO(p)
	}
	return productCategorys, totalCount, nil
}

/**
 * @Description 产品分组查询 模糊查询
 * @method
 * @auth kely
 * @Date 2023-2-23
 * @Param
 * @return
 **/
func ProductCategorysByName(offset int, limit int, name string, dic *di.Container) (productCategorys []dtos.ProductCategory, totalCount uint32, err errors.EdgeX) {
	dbClient := container.DBClientFrom(dic.Get)
	productCategoryModels, err := dbClient.ProductCategorysByName(offset, limit, name)
	if err == nil {
		totalCount = uint32(len(productCategoryModels))
	}
	if err != nil {
		return productCategorys, totalCount, errors.NewCommonEdgeXWrapper(err)
	}
	productCategorys = make([]dtos.ProductCategory, len(productCategoryModels))
	for i, d := range productCategoryModels {
		productCategorys[i] = dtos.FromProductCategoryModelToDTO(d)
	}
	return productCategorys, totalCount, nil
}

/**
 * @Description 产品分组新增方法
 * @method
 * @auth kely
 * @Date 2022-2-22
 * @Param
 * @return
 **/

func AddProductCategory(d models.ProductCategory, ctx context.Context, dic *di.Container) (id string, edgeXerr errors.EdgeX) {
	dbClient := container.DBClientFrom(dic.Get)
	lc := bootstrapContainer.LoggingClientFrom(dic.Get)
	addedProductCategory, err := dbClient.AddProductCategory(d)
	if err != nil {
		return "", errors.NewCommonEdgeXWrapper(err)
	}

	lc.Debugf(
		"Device created on DB successfully. Device ID: %s, Correlation-ID: %s ",
		addedProductCategory.Id,
		correlation.FromContext(ctx),
	)

	go publishProductCategorySystemEvent(common.DeviceSystemEventActionAdd, d.Name, d, ctx, lc, dic)

	return addedProductCategory.Id, nil
}

func publishProductCategorySystemEvent(action string, owner string, d models.ProductCategory, ctx context.Context, lc logger.LoggingClient, dic *di.Container) {
	device := dtos.FromProductCategoryModelToDTO(d)
	systemEvent := dtosPro.NewSystemEvent(common.DeviceSystemEventType, action, common.CoreMetaDataServiceKey, owner, nil, device)

	messagingClient := bootstrapContainer.MessagingClientFrom(dic.Get)
	if messagingClient == nil {
		return
	}

	config := conter.ConfigurationFrom(dic.Get)
	publishTopic := fmt.Sprintf("%s/%s/%s/%s/%s/%s",
		config.MessageQueue.PublishTopicPrefix,
		systemEvent.Source,
		systemEvent.Type,
		systemEvent.Action,
		systemEvent.Owner,
		device.Name)

	payload, _ := json.Marshal(systemEvent)
	envelope := types.NewMessageEnvelope(payload, ctx)
	envelope.ContentType = common.ContentTypeJSON

	if err := messagingClient.Publish(envelope, publishTopic); err != nil {
		lc.Errorf("unable to publish '%s' Device System Event for device '%s' to topic '%s': %v", action, device.Name, publishTopic, err)
		return
	}

	lc.Debugf("Published the '%s' Device System Event for device '%s' to topic '%s'", action, device.Name, publishTopic)
}

/**
 * @Description 产品分组修改
 * @method
 * @auth kely
 * @Date ${DATE}
 * @Param
 * @return
 **/

func PatchProductCategory(dto dtos.UpdateProductCategory, ctx context.Context, dic *di.Container) errors.EdgeX {
	dbClient := container.DBClientFrom(dic.Get)
	lc := bootstrapContainer.LoggingClientFrom(dic.Get)

	productCategory, err := productCategoryByDTO(dbClient, dto)
	oldProductName := productCategory.Name
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}

	requestDTO.ReplaceDeviceModelFieldsWithDTO(&productCategory, dto)

	err = dbClient.UpdateDevice(oldProductName, productCategory)
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}

	lc.Debugf(
		"Device patched on DB successfully. Correlation-ID: %s ",
		correlation.FromContext(ctx),
	)

	go publishProductCategorySystemEvent(common.DeviceSystemEventActionUpdate, productCategory.Name, productCategory, ctx, lc, dic)

	return nil
}

/**
 * @Description 通过id获取产品
 * @method
 * @auth kely
 * @Date 2023-2-28
 * @Param
 * @return
 **/

func productCategoryByDTO(dbClient *redis.AiotClient, dto dtos.UpdateProductCategory) (productCategory models.ProductCategory, edgeXerr errors.EdgeX) {
	// The ID or Name is required by DTO and the DTO also accepts empty string ID if the Name is provided
	if dto.Id != nil && *dto.Id != "" {
		productCategory, edgeXerr = dbClient.ProductCategoryById(*dto.Id)
		if edgeXerr != nil {
			return productCategory, errors.NewCommonEdgeXWrapper(edgeXerr)
		}
	}
	return productCategory, nil
}

/**
 * @Description 根据产品分组id-删除分组
 * @method
 * @auth kely
 * @Date 2023-3-1
 * @Param
 * @return
 **/

func DeleteProductCategoryById(id string, ctx context.Context, dic *di.Container) errors.EdgeX {
	lc := bootstrapContainer.LoggingClientFrom(dic.Get)

	if id == "" {
		return errors.NewCommonEdgeX(errors.KindContractInvalid, "id is empty", nil)
	}
	dbClient := container.DBClientFrom(dic.Get)
	productCategory, err := dbClient.ProductCategoryById(id)
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}
	err = dbClient.DeleteProductCategoryById(productCategory, id)
	if err != nil {
		return errors.NewCommonEdgeXWrapper(err)
	}

	go publishProductCategorySystemEvent(common.DeviceSystemEventActionDelete, productCategory.Name, productCategory, ctx, lc, dic)

	return nil
}
